<?php

namespace So\Library;

use Hyperf\Di\Annotation\Inject;
use Hyperf\DbConnection\Db;
use League\Flysystem\Filesystem;
use So\Library\Random;
use Hyperf\Filesystem\FilesystemFactory;

/**
 * 上传
 * @author Saopig <1306222220@qq.com>
 */
class Upload
{

    /**
     * @Inject
     * @var Filesystem
     */
    protected $filesystem;

    /**
     * @Inject
     * @var FilesystemFactory
     */
    protected $hyperfFilesystem;

    /**
     * 错误信息
     */
    protected $_error = '';

    public function pull($file, $data = [])
    {
        if (!$file) {
            $this->setError('未上传内容');
            return false;
        }
        $type = config('file.default');
        $config = config("so.upload");
        $extension = $file->getExtension();
        $size = $file->getSize();
        $minetype = $file->getMimeType();
        if (!in_array($minetype, $config['mimetype']) || !in_array($extension, $config['suffix'])) {
            $this->setError('该类型文件禁止上传');
            return false;
        }
        if (!$this->checkSize($size, $config)) {
            return false;
        }
        $info = getimagesize($file->getRealPath());
        if (!$info || !isset($info[0]) || !isset($info[1])) {
            $this->setError('该类型文件禁止上传');
            return false;
        }
        $filename = $file->getClientFilename();
        $filepath = $file->getRealPath();
        $root = trim($config['root'], '/') . '/';
        if ($config['savetype'] == 'md5') {
            $filename = md5_file($file->getRealPath()) . '.' . $extension;
        } elseif ($config['savetype'] == 'random') {
            $filename = Random::alnum('32') . '.' . $extension;
        }
        $uploadpath = $root . $filename;
        $domain = rtrim($config['domain'], '/') . '/';
        $domainpath = $domain . $uploadpath;
        $document_root = $type == 'local' ? trim(str_replace(BASE_PATH, '', config("server.settings.document_root")), '/') . '/' : '/';
        $res = Db::table('file')->where('filename', '=', $filename)->first();
        if ($res) {
            $domainpath = $res['url'];
            $id = $res['id'];
        } else {
            try {
                $stream = fopen($filepath, 'r+');
                $this->filesystem->writeStream($document_root . $uploadpath, $stream);
                is_resource($stream) && fclose($stream);
                $id = Db::table("file")->insertGetId(array_merge([
                    'path' => $document_root . $uploadpath,
                    'url' => $domainpath,
                    'filename' => $filename,
                    'filesize' => $size,
                    'mimetype' => $minetype,
                    'extension' => $extension,
                    'type' => $type,
                    'created_at' => time(),
                    'updated_at' => time(),
                ], $data));
            } catch (\Exception $e) {
                $msg = $e->getMessage();
                $this->setError($msg ?: "未知错误");
                return false;
            }
        }
        return [
            'id' => $id,
            'filename' => $filename,
            'url' => $domainpath,
        ];
    }

    public function delete($type, $path)
    {
        $local = $this->hyperfFilesystem->get($type);
        return $local->delete($path);
    }

    protected function checkSize($filesize, $config)
    {
        preg_match('/([0-9\.]+)(\w+)/', $config['maxsize'], $matches);
        $size = $matches ? $matches[1] : $config['maxsize'];
        $type = $matches ? strtolower($matches[2]) : 'b';
        $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
        $size = (int)($size * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0));
        if ($filesize > $size) {
            $filesize = round($filesize / pow(1024, 2), 2);
            $size = round($size / pow(1024, 2), 2);
            $this->setError('文件过大 (%sMiB). 不能大于: %sMiB.');
            return fasle;
        }
        return true;
    }

    public function setError($error)
    {
        $this->_error = $error;
    }

    public function getError()
    {
        return $this->_error;
    }
}
